package db.swing2;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;

import de.dnb.basics.applicationComponents.DbProperties;
import de.dnb.basics.applicationComponents.DbUtils;
import de.dnb.basics.applicationComponents.Pair;
import de.dnb.basics.applicationComponents.RangeCheckUtils;

/**
 
* Verbindung zu der Datenbank, die in D:/db/db.properties
* eingetragen ist. Die Verbindung* sollte über {@link #close()} geschlossen 
* werden ( obwohl {@link #finalize()} auch die Verbindung schließt).
*  
* @author Christian_2
*
*/
public final class DBConnection {

	private final Connection connection;
	private final PreparedStatement statementSearchAllTypes;
	private final PreparedStatement statementSearchNameType;
	private final PreparedStatement statementSearchID;
	private final PreparedStatement statementSearchUBB;

	/**
	 * Stellt eine Verbindung zu der Datenbank her, die in D:/db/db.properties
	 * eingetragen ist. Bereitet die PreparedStatements vor. Die Verbindung
	 * sollte über {@link #close()} geschlossen werden ( obwohl 
	 * {@link #finalize()} auch die Verbindung schließt).
	 * 
	 * @throws SQLException	wenn sql-Fehler
	 */
	public DBConnection() throws SQLException {
		DbProperties properties = new DbProperties("./dbconfig/db.properties");
		String url =
				properties.getUrl()
						+ ";ifexists=false;create=false;readonly=true";
		connection =
				DriverManager.getConnection(url, properties.getUserName(),
						properties.getPassword());
		statementSearchAllTypes =
				connection.prepareStatement(
						"SELECT type, level, name, id FROM sw "
								+ "WHERE name LIKE ?",
						ResultSet.TYPE_SCROLL_INSENSITIVE,
						ResultSet.CONCUR_READ_ONLY);
		statementSearchNameType =
				connection.prepareStatement("SELECT type, level, name, id "
						+ "FROM sw WHERE name LIKE ? AND type = ?",
						ResultSet.TYPE_SCROLL_INSENSITIVE,
						ResultSet.CONCUR_READ_ONLY);
		statementSearchID =
				connection.prepareStatement(
						"SELECT type, level, name, id FROM sw WHERE id = ?",
						ResultSet.TYPE_SCROLL_INSENSITIVE,
						ResultSet.CONCUR_READ_ONLY);

		statementSearchUBB =
				connection.prepareStatement("SELECT id, name "
						+ "FROM sw JOIN rel ON rel.ub = sw.id "
						+ "WHERE rel.ob = ? " + "ORDER BY name",
						ResultSet.TYPE_FORWARD_ONLY,
						ResultSet.CONCUR_READ_ONLY);

	}

	/**
	 * Erstellt das ResultSet für eine Tabelle, die die Treffer der
	 * trunkierten Suche nach einem Schlagwort enthält.
	 * 
	 * @param name	nicht null, kann aber auch leer sein, dann wird
	 * 				die gesamte Datenbank angezeigt.
	 * 
	 * @return		Modell oder null (bei sql-Fehler)
	 */
	public ResultSet getResultSetForName(final String name) {
		RangeCheckUtils.assertReferenceParamNotNull("name", name);
		ResultSet resultSetSearchName = null;
		try {
			statementSearchAllTypes.setString(1, name + '%');
			resultSetSearchName = statementSearchAllTypes.executeQuery();
			return resultSetSearchName;
		} catch (SQLException e) {
			DbUtils.safeCloseResultSet(resultSetSearchName);
			return null;
		}
	}

	/**
	 * Erstellt das ResultSet für eine Tabelle, die die Treffer der
	 * trunkierten Suche nach einem Schlagwort enthält.
	 * 
	 * @param name	nicht null, kann aber auch leer sein, dann wird
	 * 				der gesamte Bestand zu einem Typ angezeigt.
	 * @param type	Datensatztyp (p,g,s, ...). Wird nicht auf Zulässigkeit
	 * 				überprüft.
	 * 
	 * @return		Modell oder null (bei sql-Fehler)
	 */
	public ResultSet getResultSetForNameAndType(
			final String name, final char type) {
		ResultSet resultSetSearchNameAndType = null;
		try {
			statementSearchNameType.setString(1, name + '%');
			statementSearchNameType.setString(2, String.valueOf(type));
			resultSetSearchNameAndType = statementSearchNameType.executeQuery();
			return resultSetSearchNameAndType;
		} catch (SQLException e) {
			DbUtils.safeCloseResultSet(resultSetSearchNameAndType);
			return null;
		}
	}

	/**
	 * Liefert ein ResultSet für die Suche nach einer idn.
	 * 
	 * @param id	id des Datensatzes
	 * @return		Model oder null bei SQL-Fehler
	 */
	public ResultSet getResultsetForID(final String id) {
		ResultSet resultSetSearchID = null;
		try {
			statementSearchID.setString(1, id);
			resultSetSearchID = statementSearchID.executeQuery();
			return resultSetSearchID;
		} catch (SQLException e) {
			DbUtils.safeCloseResultSet(resultSetSearchID);
			return null;
		}
	}

	/**
	 * Holt zur idn den Namen aus der Tablle sw.
	 * 
	 * @param idn
	 *            nicht null
	 * @return Name oder null
	 */
	public String getNameForIDN(final String idn) {
		ResultSet resultSetSearchID = null;
		try {
			statementSearchID.setString(1, idn);
			resultSetSearchID = statementSearchID.executeQuery();
			resultSetSearchID.next();
			String name = resultSetSearchID.getString("name");
			DbUtils.safeCloseResultSet(resultSetSearchID);
			return name;
		} catch (SQLException e) {
			DbUtils.safeCloseResultSet(resultSetSearchID);
			return null;
		}
	}

	/**
	 * Gibt eine Liste von Paaren (id, name) zurück, die Unterbegriffe von obID
	 * sind.
	 * 
	 * @param obID
	 *            nicht null, nicht leer
	 * @return eventuell leere Liste
	 */
	public List<Pair<String, String>> getUBB(final String obID) {
		ResultSet resultSetSearchUBB = null;
		try {
			RangeCheckUtils.assertStringParamNotNullOrEmpty("id", obID);
			statementSearchUBB.setString(1, obID);
			resultSetSearchUBB = statementSearchUBB.executeQuery();
			List<Pair<String, String>> ubb = new LinkedList<>();
			while (resultSetSearchUBB.next()) {
				String id = resultSetSearchUBB.getString("id");
				String name = resultSetSearchUBB.getString("name");
				ubb.add(new Pair<String, String>(id, name));
			}
			DbUtils.safeCloseResultSet(resultSetSearchUBB);
			return ubb;
		} catch (SQLException e) {
			DbUtils.safeCloseResultSet(resultSetSearchUBB);
			System.err.println(e);
			return Collections.emptyList();
		}
	}

	/**
	 * Pfad der von der Wurzel bis zum aktuellen Knoten reicht (idn-Liste).
	 */
	private LinkedHashSet<String> path = new LinkedHashSet<String>();

	public static void main(String[] args) throws SQLException {
		DBConnection connection = new DBConnection();
		connection.close();
	}

	/**
	 * Schließt alle Ressourcen.
	 */
	public void close() {

		DbUtils.safeCloseStatement(statementSearchAllTypes);
		DbUtils.safeCloseStatement(statementSearchID);
		DbUtils.safeCloseStatement(statementSearchNameType);
		DbUtils.safeCloseStatement(statementSearchUBB);

		DbUtils.safeCloseConnection(connection);

	}

	@Override
	protected void finalize() throws Throwable {
		super.finalize();
		close();
	}

}
